/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package com.dotexe.webframework.core;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Date;
import java.util.Iterator;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import com.dotexe.webframework.core.communication.CallBackResult;
import com.dotexe.webframework.core.communication.CallBackResultCollection;
import com.dotexe.webframework.core.communication.ErrorMessage;
import com.dotexe.webframework.core.eventargs.DataUpdate;
import com.dotexe.webframework.core.eventargs.DataUpdateCollection;
import com.dotexe.webframework.core.eventargs.RefreshStateArgs;

/**
 * 
 * @author jovan.tomasevic
 */
public class ApplicationService extends HttpServlet {

	public Date LastActivity;
	public static boolean ignoreStandardProcessRequest;

	protected void processRequest(HttpServletRequest request,
			HttpServletResponse response) throws ServletException, IOException,
			Exception {
		CallBackResultCollection res = new CallBackResultCollection();
		try {

			// set session object to global variable
			Session.session = request.getSession(true);
			Session.response = response;
			// get paremeters in json string format
			String jsonParams = getJsonParams(request);
			RefreshStateArgs params = MapJson(jsonParams);
			// create controlller if ont exist
			if (ApplicationController.getInstance() == null) {
				CreateController(params);
			}
			// get url
			String url = request.getRequestURI();
			// find out requested action
			String method = getMehodName(url);
			// resolve method call
			if (method.equals("LoadUseCase")) {
				res = this.Init(params);
			} else if (method.equals("RefreshState")) {
				res = this.RefreshState(params);
			} else if (method.equals("RemoveControlor")) {
				this.RemoveController(params.controllerElementId);
			}
		} catch (Exception ex) {
			CallBackResult cb = new CallBackResult();
			ErrorMessage message = new ErrorMessage();
			message.setCaption("Error");
			message.setText(generateExMessage(ex) + "</br>Stack trace:"
					+ generateStackTraceText(ex));
			message.setExceptionType(ex.toString());
			cb.setError(message);
			res.put(cb);
		}
		if (!ignoreStandardProcessRequest) {
			// set output content type
			response.setContentType("application/json; charset=UTF-8");
			// response.setContentType("text/html;charset=UTF-8");
			PrintWriter out = response.getWriter();
			String jsonString = res.toString();

			out.append(jsonString);
			// JSONObject obj = new JSONObject();
			ApplicationController.getInstance().clearResponse();
			out.close();
		} else {
			response.getOutputStream().flush();
		}
		ignoreStandardProcessRequest = false;
	}

	private String generateStackTraceText(Exception ex) {
		String message = new String();
		for (int i = 0; i < ex.getStackTrace().length; i++) {
			message = message + "</br>" + ex.getStackTrace()[i].getClassName()
					+ "." + ex.getStackTrace()[i].getMethodName() + "."
					+ ex.getStackTrace()[i].getLineNumber();
		}
		return message;
	}

	private String generateExMessage(Exception ex) {
		String message = new String();

		message = "Message:</br>->" + ex.getMessage();
		Throwable innerEx = ex.getCause();
		while (innerEx != null) {
			message = message + "</br>->" + innerEx.getMessage();
			innerEx = innerEx.getCause();
		}

		return message;
	}

	/**
	 * <summary> Gets or sets the Controllers. </summary> <value>The
	 * Controllers.</value>
	 */
	public static ApplicationControllerCollection getControllers()
			throws Exception {
		HttpSession session = Session.session;
		if (session == null) {
			return null;
		}
		ApplicationControllerCollection controlorCollection = (ApplicationControllerCollection) session
				.getAttribute("ApplicationControllerCollection");
		if (controlorCollection == null) {
			controlorCollection = new ApplicationControllerCollection();
			session.setAttribute("ApplicationControllerCollection",
					controlorCollection);
		}
		return controlorCollection;
	}

	public static void CreateController(RefreshStateArgs req) throws Exception {
		ApplicationController Controller = new ApplicationController();
		Controller.elementId = req.controllerElementId;
		ApplicationService.getControllers().add(Controller);
		ApplicationController.setInstance(Controller);
	}

	public CallBackResultCollection RefreshState(RefreshStateArgs args)
			throws Exception {
		CallBackResultCollection res = new CallBackResultCollection();
		ApplicationController.setInstance(getControllers().get(
				args.controllerElementId));
		// LastActivity = Calendar.getInstance().getTime();
		res = ApplicationController.getInstance().refreshState(args);
		return res;
	}

	public CallBackResultCollection Init(RefreshStateArgs args)
			throws Exception {
		args.isInit = true;
		return this.RefreshState(args);
	}

	public void RemoveController(String ControllerElementId) throws Exception {
		ApplicationController Controller = ApplicationService.getControllers()
				.get(ControllerElementId);
		if (Controller != null) {
			ApplicationService.getControllers().remove(Controller);
		}
	}

	/**
	 * Get method name from url.
	 * 
	 * @param url
	 *            url from HttpRequest object
	 * @return method name
	 */
	protected String getMehodName(String url) {
		String[] res = url.split("/");
		return res[res.length - 1];
	}

	/**
	 * Read from request reader into string.
	 * 
	 * @param HttpRequest
	 *            object
	 * @return json string
	 * @throws java.io.IOException
	 */
	protected String getJsonParams(HttpServletRequest request)
			throws java.io.IOException {
		StringBuilder completeRequest = new StringBuilder();
		String jsonLine = null;
		BufferedReader reader = request.getReader();
		while ((jsonLine = reader.readLine()) != null) {
			completeRequest.append(jsonLine);
		}

		// char[] cbuf = new char[1024];
		// reader.read(cbuf);
		// json = String.valueOf(cbuf);
		reader.close();
		return completeRequest.toString();
	}

	/**
	 * Create RefreshStateArgs object from string json
	 * 
	 * @param string
	 *            json
	 * @return RefreshStateArgs object
	 * @throws JSONException
	 */
	protected RefreshStateArgs MapJson(String json) throws JSONException {
		RefreshStateArgs args = new RefreshStateArgs();
		JSONObject jsonObj = new JSONObject(json);
		JSONObject jsonArgs = jsonObj.getJSONObject("args");
		for (Iterator i = jsonArgs.sortedKeys(); i.hasNext();) {

			String key = i.next().toString();

			if (key.equals("ApplicationName")) {
				args.applicationName = jsonArgs.getString("ApplicationName");
			}
			if (key.equals("Name")) {
				args.name = jsonArgs.getString("Name");
			} else if (key.equals("ControllerElementId")) {
				args.controllerElementId = jsonArgs
						.getString("ControllerElementId");
			} else if (key.equals("ElementID")) {
				args.elementID = jsonArgs.getString("ElementID");
			} else if (key.equals("HolderID")) {
				args.holderID = jsonArgs.getString("HolderID");
			} else if (key.equals("ControlID")) {
				args.controlID = jsonArgs.getString("ControlID");
			} else if (key.equals("ControlType")) {
				args.controlType = jsonArgs.getString("ControlType");
			} else if (key.equals("DataMemberName")) {
				args.dataMemberName = jsonArgs.getString("DataMemberName");
			} else if (key.equals("DataSourceName")) {
				args.dataSourceName = jsonArgs.getString("DataSourceName");
			} else if (key.equals("EventName")) {
				args.eventName = jsonArgs.getString("EventName");
			} else if (key.equals("RequestId")) {
				args.requestId = jsonArgs.getInt("RequestId");
			} else if (key.equals("Value")) {
				args.value = jsonArgs.get("Value");
			} else if (key.equals("DataUpdates")) {
				args.dataUpdates = new DataUpdateCollection();
				JSONArray data = jsonArgs.getJSONArray("DataUpdates");
				for (int j = 0; j < data.length(); j++) {
					JSONObject obj = data.getJSONObject(j);
					DataUpdate updateData = new DataUpdate();
					updateData.dataMemberName = obj.getString("DataMemberName");
					updateData.dataSourceName = obj.getString("DataSourceName");
					updateData.value = obj.get("Value");
					args.dataUpdates.add(updateData);
				}
			} else if (key.equals("ClientOperationName")) {
				args.clientOperationName = jsonArgs
						.getString("ClientOperationName");
			} else if (key.equals("SelectedItemId")) {
				Object sid = jsonArgs.get("SelectedItemId");
				// !?
				// TODO: jovan.tomasevic
				// this if is because problem when first node in tree is
				// selected. in that case
				// jsonArgs.selectedItemId = "xnode-158";
				// !?
				if (sid != null && sid != JSONObject.NULL) {
					if (sid.getClass().getSimpleName()
							.equals(String.class.getSimpleName())) {
						args.selectedItemId = 0;
					} else {
						args.selectedItemId = jsonArgs.getInt("SelectedItemId");
					}
				}
			} else if (key.equals("SelectedItemIds")) {
				JSONArray arr = jsonArgs.getJSONArray("SelectedItemIds");
				long[] sItems = new long[arr.length()];
				for (int j = 0; j < arr.length(); j++) {
					sItems[j] = arr.getLong(j);
				}
				args.selectedItemIds = sItems;
			}

			/*
			 * else if(key.equals("PositionY")){ args.PositionY =
			 * jsonArgs.getString("PositionY"); } else
			 * if(key.equals("RowIndex")){ args.RowIndex =
			 * jsonArgs.getInt(("RowIndex")); } else if(key.equals("Value")){
			 * args.Value = jsonArgs.get("Value"); }
			 */
		}
		return args;
	}

	/**
	 * Handles the HTTP <code>GET</code> method.
	 * 
	 * @param request
	 *            servlet request
	 * @param response
	 *            servlet response
	 * @throws ServletException
	 *             if a servlet-specific error occurs
	 * @throws IOException
	 *             if an I/O error occurs
	 */
	@Override
	protected void doGet(HttpServletRequest request,
			HttpServletResponse response) throws ServletException, IOException {
		try {
			processRequest(request, response);
		} catch (Exception ex) {
			Logger.getLogger(ApplicationService.class.getName()).log(
					Level.SEVERE, null, ex);
		}
	}

	/**
	 * Handles the HTTP <code>POST</code> method.
	 * 
	 * @param request
	 *            servlet request
	 * @param response
	 *            servlet response
	 * @throws ServletException
	 *             if a servlet-specific error occurs
	 * @throws IOException
	 *             if an I/O error occurs
	 */
	@Override
	protected void doPost(HttpServletRequest request,
			HttpServletResponse response) throws ServletException, IOException {
		try {
			processRequest(request, response);
		} catch (Exception ex) {
			Logger.getLogger(ApplicationService.class.getName()).log(
					Level.SEVERE, null, ex);
		}
	}
}
